from fastapi import FastAPI,Request, Body
from pydantic import BaseModel
import socket
import win32print


#定义提交消息模型
class Zpl_request(BaseModel):
    model_no: str
    print_params: Dict[str,str]
    print_mode: Dict[str,str]


app = FastAPI()

#定义发送到网络打印服务器函数，须支持ZPL打印指令
def zpl_print_lan(zpl_data1, zpl_dict, printer_ip, printer_port = 9100):
    zpl_data = zpl_data1.format_map(zpl_dict)
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect((printer_ip, printer_port))
            s.sendall(zpl_data.encode('ascii'))
            print("打印指令已发送到打印机。")
    except socket.error as e:
        print(f"连接到打印机时发生错误: {e}") 

#定义发送到本地usb默认打印机，须支持ZPL打印指令
def zpl_print_usb(zpl_data1,zpl_dict):
    zpl_data = zpl_data1.format_map(zpl_dict)
    zpl_stream = io.BytesIO(zpl_data.encode('UTF-8'))

     # 打开默认打印机的句柄
    printer_name = win32print.GetDefaultPrinter()
    handle = win32print.OpenPrinter(printer_name)

    try:
        # 由于不能直接设置 RAW 数据类型，在这里传递 None 和空字符串作为作业信息
        # 确保在打印作业中正确地发送了原始数据
        job_id= win32print.StartDocPrinter(handle, 1, ('zpl_data',None,'RAW'))
        # 获取打印作业的页面信息（对于RAW 数据，这通常不需要，仍然需要调用 StartPagePrinter）
        win32print.StartPagePrinter(handle)
         
        # 发送数据到打印机（直接从字节流中读取）
        bytes_to_send = zpl_stream.getbuffer()
        bytes_written = win32print.WritePrinter(handle, bytes_to_send)
         
        # 确保所有数据都已发送
        assert bytes_written == len(bytes_to_send)
         
        # 结束页面和打印作业
        win32print.EndPagePrinter(handle)
        win32print.EndDocPrinter(handle)
    finally:
        # 关闭打印机句柄
        win32print.ClosePrinter(handle)
         
         # 关闭字节流（虽然在这个例子中它会自动关闭，但显式关闭是一个好习惯）
         #zpl_stream.close()

     #print(f"ZPL 指令已发送到打印机 {printer_name}")


#上传ZPL打印模板
@app.post("/zpl")
async def post_zpl(model_no: str, zpl_data: str = Body(..., media_type="text/plain")):
    model_file = f"model_zpl/{model_no}.txt"
    with open(model_file, 'w', encoding='utf-8') as f:
        f.write(zpl_data)
    return {"model_no":model_no,"zpl_data":zpl_data}

#调用打印
@app.post("/zpl_print")
async def post_zpl_print(zpl_params:Zpl_request):
    print(zpl_params.model_no, zpl_params.print_mode, zpl_params.print_params)
    zpl_data=""
    model_file = f"model_zpl/{zpl_params.model_no}.txt"
    with open(model_file, 'r') as file:
        zpl_data = file.read()
    print_mode_info = zpl_params.print_mode
    if print_mode_info["usb_lan"] == "usb":
        try:
            zpl_print_usb(zpl_data, zpl_params.print_params)
            return "zpl_print_usb_ok"
        except Exception as e:
            raise HTTPException(status_code=500, detail=f"zpl_usbprint failed: {str(e)}")
    else:
        try:
            zpl_print_lan(zpl_data, zpl_params.print_params, print_mode_info["ip"])
            return "zpl_print_lan_ok"
        except Exception as e:
            raise HTTPException(status_code=500, detail=f"zpl_print_lan failed: {str(e)}")